﻿using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using OpenAiMobile.Util.GZip;
using OpenAiMobile.Util.Io;

namespace OpenAiMobile.Util.Security
{
    public static class CustomSafeDataHelper2
    {
        /// <summary>
        /// 加密字符串
        /// </summary>
        /// <param name="value">待加密的字符串</param>
        /// <param name="encryptKey">加密密钥</param>
        /// <returns>加密成功返回加密后的字符串,失败返回空</returns>
        public static string Encrypt(string value, string encryptKey)
        {
            if (string.IsNullOrWhiteSpace(value) || encryptKey == null)
            {
                return string.Empty;
            }

            var zippedData = GZipHelper.GZipCompressString(value); //GZip压缩
            var data = Encoding.UTF8.GetBytes(zippedData);
            var key = Encoding.UTF8.GetBytes(encryptKey);
            var buffer = Encrypt(data, key); //AES加密
            return buffer.ToBase64();
        }

        /// <summary>
        /// 解密字符串
        /// </summary>
        /// <param name="value">待解密的字符串</param>
        /// <param name="decryptKey">解密密钥</param>
        /// <returns>解密成功返回解密后的字符串,失败返回空</returns>
        public static string Decrypt(string value, string decryptKey)
        {
            if (string.IsNullOrWhiteSpace(value) || decryptKey == null)
            {
                return string.Empty;
            }

            var data = value.ToBase64(); //将Base64编码字符串转为字节数组
            var key = Encoding.UTF8.GetBytes(decryptKey);
            var buffer = Decrypt(data, key); //AES解密
            var zippedData = buffer.ToStr(Encoding.UTF8);
            return GZipHelper.GZipDecompressString(zippedData); //GZip解压缩
        }

        public static byte[] Encrypt(byte[] data, byte[] key)
        {
            using (var aes = Aes.Create())
            {
                return Encrypt(aes, data, key);
            }
        }

        public static byte[] Decrypt(byte[] data, byte[] key)
        {
            using (var aes = Aes.Create())
            {
                return Decrypt(aes, data, key);
            }
        }

        public static byte[] Encrypt(
            this SymmetricAlgorithm sa,
            byte[] data,
            byte[] pass = null,
            CipherMode mode = CipherMode.CBC,
            PaddingMode padding = PaddingMode.PKCS7
        )
        {
            if (data == null || data.Length == 0)
            {
                throw new ArgumentNullException(nameof(data));
            }

            if (pass != null && pass.Length != 0)
            {
                sa.Key = sa.LegalKeySizes.Length == 0 ? pass : SecurityHelper.Pad(pass, sa.LegalKeySizes[0]);
                var dst = new byte[sa.IV.Length];
                dst.Write(0, pass);
                sa.IV = dst;
                sa.Mode = mode;
                sa.Padding = padding;
            }

            // ReSharper disable once IdentifierTypo
            var encryptor = sa.CreateEncryptor();

            using (var memoryStream = new MemoryStream())
            using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
            {
                cryptoStream.Write(data, 0, data.Length);
                if (sa.Padding == PaddingMode.None)
                {
                    var num = data.Length % 8;
                    if (num > 0)
                    {
                        var buffer = new byte[8 - num];
                        cryptoStream.Write(buffer, 0, buffer.Length);
                    }
                }

                cryptoStream.FlushFinalBlock();
                return memoryStream.ToArray();
            }
        }

        /// <summary>对称解密算法扩展</summary>
        /// <remarks>CBC填充依赖IV，要求加解密的IV一致，而ECB填充则不需要</remarks>
        /// <param name="sa">算法</param>
        /// <param name="data">数据</param>
        /// <param name="pass">密码</param>
        /// <param name="mode">模式。.Net默认CBC，Java默认ECB</param>
        /// <param name="padding">填充算法。默认PKCS7，等同Java的PKCS5</param>
        /// <returns></returns>
        public static byte[] Decrypt(
            this SymmetricAlgorithm sa,
            byte[] data,
            byte[] pass = null,
            CipherMode mode = CipherMode.CBC,
            PaddingMode padding = PaddingMode.PKCS7
        )
        {
            if (data == null || data.Length == 0)
            {
                throw new ArgumentNullException(nameof(data));
            }

            if (pass != null && pass.Length != 0)
            {
                sa.Key = sa.LegalKeySizes.Length == 0 ? pass : SecurityHelper.Pad(pass, sa.LegalKeySizes[0]);
                var dst = new byte[sa.IV.Length];
                dst.Write(0, pass);
                sa.IV = dst;
                sa.Mode = mode;
                sa.Padding = padding;
            }

            // ReSharper disable once IdentifierTypo
            var decryptor = sa.CreateDecryptor();

            using (var memoryStream = new MemoryStream(data))
            using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
            using (var srDecrypt = new StreamReader(cryptoStream))
            {
                return Encoding.UTF8.GetBytes(srDecrypt.ReadToEnd());
            }
        }
    }
}
